/*
 * 代号：凤凰
 * http://www.jphenix.org
 * 2018年4月25日
 * V4.0
 */
package com.jphenix.driver.dbshell;

import com.jphenix.share.lang.SString;
import com.jphenix.standard.docs.ClassInfo;

import java.sql.*;
import java.util.Properties;
import java.util.logging.Logger;

/**
 * 数据库驱动外壳类
 * 
 * 可以拦截数据库操作
 * 
 * JDBC数据库驱动类路径
 * com.jphenix.driver.dbshell.Driver
 * 
 * 当前JDBCURL拼装方法
 * 
 * jdbc:jphenix:shell:driver=[目标数据库驱动类路径]#url=[目标URL]
 * 
 * 注意：原本采用 &url=  但是在html或xml中 &符号是特殊转义符号的前缀，比如 &nbsp; 所以改为用井号
 *       url参数要放在末尾
 * 
 * JDBC驱动原理：
 * 
 * 在执行以下语句初始化数据库连接对象时：
 * Class.forName(driveNameStr).newInstance(); 
 * 
 * 数据库驱动会构造一个静态的类实例，然后通过 DriverManager.registerDriver(INSTANCE); 方法
 * 将当前类实例注册到数据库驱动管理类中。（实际上是放到了数据库驱动管理类中的Vector中）
 * 
 * 再在执行 conn = DriverManager.getConnection(urlStr,userStr,passWordStr); 获取数据库连接时
 * 
 * 数据库驱动管理类会遍历Vector中所有的数据库驱动类实例，挨个问，这个URL你能搞定不。大部分数据库
 * 驱动类实例看了一眼，一头雾水，然后返回null，直到启动一个驱动类实例返回了数据库连接类实例。
 * 
 * 纠结了一下：是否支持多个处理事件，从业务逻辑角度来看，是支持的，但是从性能角度来看，每次执行时都遍历多个处理事件
 *             效率会降低很多，而且通常情况下，只会放入一个数据处理事件类，放入多个也容易引起混乱。
 *             
 * 最终：只支持一个数据处理事件类
 * 
 * 使用方法；
 * 
 * 		编写一个常驻内存的处理数据的类，实现IStatementEvent接口.
 * 		在这个类初始化时，需要执行 (new EventVO()).setEvent(this);  将当前类实例设置到拦截事件处理容器中。
 * 
 * 		同时，DriverUtil 中有一些常用的方法可以用到
 * 		
 * 
 * @author MBG
 * 2018年4月25日
 */
@ClassInfo({"2017-04-25 16:38","数据库驱动外壳类"})
public class Driver implements java.sql.Driver {

    private final static Driver INSTANCE   = new Driver();   //当前类实例
    private volatile boolean hasRegistered  = false;         //是否已经注册到驱动管理类
    
    /**
     * 构造函数
     * @author MBG
     */
    static {
        load();
    }
    
    /**
     * 覆盖方法
     * @param url  格式：driver=[目标数据库驱动类路径]#url=[目标URL]
     * @param info 内容：key:user value:[目标数据库登录名]  key:password value:[目标数据库登录密码]
     */
    @Override
    public Connection connect(String url, Properties info) throws SQLException {
        //判断是否为当前驱动所适配的URL并
        //从当前URL中获取目标驱动及url信息 0数据源主键  1驱动类路径 2url
        String[] infos = DriverUtil.fixTargetInfo(url);
        if(infos==null) {
            return null;
        }
        //目标数据库登录名
        String user = SString.valueOf(info.getProperty("user"));
        //目标数据库登录密码
        String pwd  = SString.valueOf(info.getProperty("password"));
        if(infos[0]==null || infos[0].length()<1 || infos[1]==null 
                || infos[1].length()<1 || infos[2]==null || infos[2].length()<1 
                || user.length()<1) {
            //拼装错误信息
            String errorMsg = "The DataBaseDriverShell ["+this.getClass().getName()+"] Init Info Is Wrong.\n"
                    +"The Right Linked URL Like This For "+DriverUtil.URL_HEADER+"source=[DataBase Source ID]#dirver=[Target Driver Class Path]#url=[Target URL For Driver]\n"
                    +"This [user] And [password] Is Used For Target Driver\n"
                    +"Current Wrong URL:["+url+"] Or Wrong Login Info:["+info+"]\n";
            System.err.println(errorMsg);
            throw new SQLException(errorMsg);
        }
        Connection conn = null; //返回值
        try {
            //构造新实例
            Class.forName(infos[1]).newInstance(); 
            conn = DriverManager.getConnection(infos[2],user,pwd);
        }catch(Exception e) {
            e.printStackTrace();
            try {
                conn.close();
            }catch(Exception e2) {}
        }
        //返回封装后的连接对象
        return new ConnectionImpl(infos[0],conn,new EventVO());
    }
    
    /**
     * 判断获取数据用的URL是否为该驱动解析的URL
     */
    @Override
    public boolean acceptsURL(String url) throws SQLException {
        return url != null && url.toLowerCase().startsWith(DriverUtil.URL_HEADER);
    }

    /**
     * 这个方法从来就没用过
     */
    @Override
    public DriverPropertyInfo[] getPropertyInfo(String url, Properties info) throws SQLException {
        return new DriverPropertyInfo[0];
    }

    /**
     * 获取主版本号
     */
    @Override
    public int getMajorVersion() {
        return 1;
    }

    /**
     * 获取子版本号
     */
    @Override
    public int getMinorVersion() {
        return 0;
    }

    /**
     * 覆盖方法
     */ 
    @Override
    public boolean jdbcCompliant() {
        return true;
    }

    /**
     * 往数据库驱动管理类中注册了当前驱动
     * @return 当前驱动类实例
     */
    private static synchronized Driver load() {
        try {
            if (!INSTANCE.hasRegistered) {
                INSTANCE.hasRegistered = true;
                DriverManager.registerDriver(INSTANCE);
            }
        } catch (SQLException e) {
            e.printStackTrace();
        }
        return INSTANCE;
    }

	/**
	 * JDK1.7需要实现的方法
	 */
	@Override
    public Logger getParentLogger() throws SQLFeatureNotSupportedException {
		return null;
	}
}
